package com.znjc.device.service;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.znjc.common.constant.DeviceConstants;
import com.znjc.device.domain.IotSerial;
import com.znjc.device.util.CRC16M;
import com.znjc.device.websocket.WebSocketUsers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * 串口通信服务类
 * 负责串口的打开、关闭、数据发送和接收
 */
@Slf4j
@Service
public class SerialPortService {

    private SerialPort serialPort;
    private volatile boolean isPortOpen = false;
    private BlockingQueue<String> responseQueue = new LinkedBlockingQueue<>();
    private StringBuilder currentResponse = new StringBuilder();

    /**
     * 打开串口
     *
     * @param iotSerial 串口配置信息
     * @return 是否打开成功
     */
    public boolean openSerialPort(IotSerial iotSerial) {
        try {
            // 根据串口号获取串口对象
            serialPort = SerialPort.getCommPort(iotSerial.getSerialCode());

            // 配置串口参数
            serialPort.setBaudRate(iotSerial.getBaudRate());
            serialPort.setNumDataBits(iotSerial.getDataBit());
            serialPort.setNumStopBits(iotSerial.getStopBit());
            serialPort.setParity(iotSerial.getCheckoutBit());

            // 设置读取超时
            serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING, 1000, 0);

            // 打开串口
            if (serialPort.openPort()) {
                log.info("串口 {} 打开成功", iotSerial.getSerialCode());
                isPortOpen = true;
                // 添加数据监听器
                serialPort.addDataListener(new SerialPortDataListener() {
                    @Override
                    public int getListeningEvents() {
                        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
                    }

                    @Override
                    public void serialEvent(SerialPortEvent event) {
                        if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
                            readDataFromSerialPort();
                        }
                    }
                });

                return true;
            } else {
                log.error("串口 {} 打开失败", iotSerial.getSerialCode());
                return false;
            }
        } catch (Exception e) {
            log.error("打开串口异常: {}", e.getMessage(), e);
            return false;
        }
    }

    // 字节数组转字符串
    private String printHexString(byte[] b) {
        StringBuffer sbf = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sbf.append(hex.toUpperCase() + "  ");
        }
        return sbf.toString().trim();
    }
    /**
     * 从串口读取数据
     */
    private void readDataFromSerialPort() {
        try {
            byte[] buffer = new byte[1024];
            int bytesRead = serialPort.readBytes(buffer, buffer.length);
            if (bytesRead > 0) {
                String receivedData = printHexString(buffer);
                currentResponse.append(receivedData);
                log.debug("接收到串口数据: {}", receivedData);
                WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996,"接收到串口数据: {}"+ receivedData);
                String completeResponse = currentResponse.toString().trim();
                if (!completeResponse.isEmpty()) {
                    responseQueue.offer(completeResponse);
                    currentResponse.setLength(0); // 清空当前响应
                }

            }
        } catch (Exception e) {
            log.error("读取串口数据异常: {}", e.getMessage(), e);
            WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996,"读取串口数据异常: {}"+ e.getMessage());
        }
    }

    /**
     * 向串口发送命令
     *
     * @param command 要发送的命令
     * @return 是否发送成功
     */
    public boolean sendCommand(String command) {
        if (!isPortOpen || serialPort == null) {
            log.error("串口未打开，无法发送命令");
            return false;
        }

        try {
            // 清空之前的响应数据
            responseQueue.clear();
            currentResponse.setLength(0);

            byte[] b = CRC16M.getSendBuf(command);
            serialPort.getOutputStream().write(b);
            // 将字节数组转换为带空格的十六进制字符串
            StringBuilder hexStr = new StringBuilder();
            for (byte value : b) {
                hexStr.append(String.format("%02X ", value)); // 格式化为两位大写十六进制，加空格分隔
            }
            log.debug("命令发送成功: {}", hexStr.toString().trim());
            WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996,"命令发送成功: {}"+hexStr.toString().trim());
            return true;
        } catch (Exception e) {
            log.error("发送命令异常: {}", e.getMessage(), e);
            WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996,"发送命令异常: {}"+ e.getMessage());
            return false;
        }
    }

    /**
     * 等待响应数据
     *
     * @param timeout 超时时间（秒）
     * @return 响应数据，超时返回null
     */
    public String waitForResponse(int timeout) {
        try {
            // 等待指定的超时时间
            String response = responseQueue.poll(timeout, TimeUnit.SECONDS);
            if (response != null) {
                log.debug("收到响应: {}", response);
            } else {
                log.warn("等待响应超时");
            }
            return response;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("等待响应被中断");
            return null;
        }
    }

    /**
     * 关闭串口
     */
    public void closeSerialPort() {
        if (serialPort != null && isPortOpen) {
            serialPort.removeDataListener();
            serialPort.closePort();
            isPortOpen = false;
            log.info("串口已关闭");
        }
    }

    /**
     * 检查串口是否打开
     */
    public boolean isPortOpen() {
        return isPortOpen;
    }
}